package cn.edu.sgu.www.mhxysy.service.role.impl;

import cn.edu.sgu.www.mhxysy.pager.Pager;
import cn.edu.sgu.www.mhxysy.consts.IdentifierPrefixes;
import cn.edu.sgu.www.mhxysy.consts.MimeTypes;
import cn.edu.sgu.www.mhxysy.dto.role.RoleAccountDTO;
import cn.edu.sgu.www.mhxysy.easyexel.impl.RoleAccountExcelData;
import cn.edu.sgu.www.mhxysy.entity.except.Account;
import cn.edu.sgu.www.mhxysy.entity.except.JiadianSchema;
import cn.edu.sgu.www.mhxysy.entity.gang.Gang;
import cn.edu.sgu.www.mhxysy.entity.role.RoleAccount;
import cn.edu.sgu.www.mhxysy.entity.role.RoleAttribute;
import cn.edu.sgu.www.mhxysy.entity.role.RoleJiadian;
import cn.edu.sgu.www.mhxysy.entity.role.RoleCategory;
import cn.edu.sgu.www.mhxysy.entity.school.School;
import cn.edu.sgu.www.mhxysy.entity.server.Server;
import cn.edu.sgu.www.mhxysy.enums.OpenStatus;
import cn.edu.sgu.www.mhxysy.enums.UnlockStatus;
import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.mhxysy.factory.impl.RoleAttributeFactory;
import cn.edu.sgu.www.mhxysy.mapper.except.AccountMapper;
import cn.edu.sgu.www.mhxysy.mapper.except.JiadianSchemaMapper;
import cn.edu.sgu.www.mhxysy.mapper.exp.RoleExpMapper;
import cn.edu.sgu.www.mhxysy.mapper.gang.GangMapper;
import cn.edu.sgu.www.mhxysy.mapper.role.RoleAccountMapper;
import cn.edu.sgu.www.mhxysy.mapper.role.RoleAttributeMapper;
import cn.edu.sgu.www.mhxysy.mapper.role.RoleJiadianMapper;
import cn.edu.sgu.www.mhxysy.mapper.role.RoleCategoryMapper;
import cn.edu.sgu.www.mhxysy.mapper.school.SchoolMapper;
import cn.edu.sgu.www.mhxysy.mapper.school.SchoolSkillMapper;
import cn.edu.sgu.www.mhxysy.mapper.server.ServerMapper;
import cn.edu.sgu.www.mhxysy.redis.repository.impl.RoleAccountRepository;
import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
import cn.edu.sgu.www.mhxysy.service.role.RoleAccountService;
import cn.edu.sgu.www.mhxysy.util.CollectionUtils;
import cn.edu.sgu.www.mhxysy.util.HttpUtils;
import cn.edu.sgu.www.mhxysy.util.StringUtils;
import cn.edu.sgu.www.mhxysy.vo.role.RoleAccountVO;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 沐雨橙风ιε
 * @version 1.0
 */
@Service
public class RoleAccountServiceImpl implements RoleAccountService {

	private final GangMapper gangMapper;
	private final SchoolMapper schoolMapper;
	private final ServerMapper serverMapper;
	private final AccountMapper accountMapper;
	private final RoleExpMapper roleExpMapper;
	private final RoleAccountMapper roleAccountMapper;
	private final RoleJiadianMapper roleJiadianMapper;
	private final SchoolSkillMapper schoolSkillMapper;
	private final RoleCategoryMapper roleCategoryMapper;
	private final RoleAttributeMapper roleAttributeMapper;
	private final JiadianSchemaMapper jiadianSchemaMapper;
	private final RoleAccountRepository roleAccountRepository;

	@Autowired
	public RoleAccountServiceImpl(
			GangMapper gangMapper,
			SchoolMapper schoolMapper,
			ServerMapper serverMapper,
			AccountMapper accountMapper,
			RoleExpMapper roleExpMapper,
			RoleAccountMapper roleAccountMapper,
			RoleJiadianMapper roleJiadianMapper,
			SchoolSkillMapper schoolSkillMapper,
			RoleCategoryMapper roleCategoryMapper,
			RoleAttributeMapper roleAttributeMapper,
			JiadianSchemaMapper jiadianSchemaMapper,
			RoleAccountRepository roleAccountRepository) {
		this.gangMapper = gangMapper;
		this.schoolMapper = schoolMapper;
		this.serverMapper = serverMapper;
		this.accountMapper = accountMapper;
		this.roleExpMapper = roleExpMapper;
		this.roleAccountMapper = roleAccountMapper;
		this.roleJiadianMapper = roleJiadianMapper;
		this.schoolSkillMapper = schoolSkillMapper;
		this.roleCategoryMapper = roleCategoryMapper;
		this.roleAttributeMapper = roleAttributeMapper;
		this.jiadianSchemaMapper = jiadianSchemaMapper;
		this.roleAccountRepository = roleAccountRepository;
	}

	@Override
	public void exportData() throws UnsupportedEncodingException {
		String fileName = StringUtils.encode("角色列表.xlsx");
		HttpServletResponse response = HttpUtils.getResponse();

		response.setCharacterEncoding("utf-8");
		response.setContentType(MimeTypes.APPLICATION_SHEET);
		response.setHeader("Content-disposition", "attachment;filename=" + fileName);

		try (ServletOutputStream outputStream = response.getOutputStream()) {
			List<RoleAccount> roleAccounts = roleAccountMapper.selectList(null);

			List<RoleAccountExcelData> excelData = roleAccounts.stream().map(new Function<RoleAccount, RoleAccountExcelData>() {
				@Override
				public RoleAccountExcelData apply(RoleAccount roleAccount) {
					return toExcelData(roleAccount);
				}
			}).collect(Collectors.toList());

			// 帮派
			List<Gang> gangs = gangMapper.selectList(null);
			// 门派
			List<School> schools = schoolMapper.selectList(null);
			// 账号
			List<Account> accounts = accountMapper.selectList(null);
			// 角色造型
			List<RoleCategory> modelings = roleCategoryMapper.selectList(null);

			// 要导出的总记录数
			int size = roleAccounts.size();

			// 给指定列添加下拉框
			SheetWriteHandler writeHandler = new SheetWriteHandler() {
				@Override
				public void afterSheetCreate(SheetWriteHandlerContext context) {
					Sheet sheet = context.getWriteSheetHolder().getSheet();
					DataValidationHelper helper = sheet.getDataValidationHelper();

					// 角色造型
					sheet.addValidationData(helper.createValidation(helper.createExplicitListConstraint(modelings.stream().map(new Function<RoleCategory, String>() {
						@Override
						public String apply(RoleCategory roleModeling) {
							return roleModeling.getName();
						}
					}).toArray(String[]::new)), new CellRangeAddressList(1, size, 2, 2)));

					// 门派
					sheet.addValidationData(helper.createValidation(helper.createExplicitListConstraint(schools.stream().map(new Function<School, String>() {
						@Override
						public String apply(School school) {
							return school.getName();
						}
					}).toArray(String[]::new)), new CellRangeAddressList(1, size, 5, 5)));

					// 帮派
					sheet.addValidationData(helper.createValidation(helper.createExplicitListConstraint(gangs.stream().map(new Function<Gang, String>() {
						@Override
						public String apply(Gang gang) {
							return gang.getName();
						}
					}).toArray(String[]::new)), new CellRangeAddressList(1, size, 7, 7)));

					// 账号
					sheet.addValidationData(helper.createValidation(helper.createExplicitListConstraint(accounts.stream().map(new Function<Account, String>() {
						@Override
						public String apply(Account account) {
							return account.getAccount();
						}
					}).toArray(String[]::new)), new CellRangeAddressList(1, size, 9, 9)));
				}
			};

			EasyExcel.write(outputStream, RoleAccountExcelData.class).sheet("角色列表")
					.registerWriteHandler(writeHandler)
					.doWrite(excelData);
		} catch (Exception e) {
			e.printStackTrace();

			HttpUtils.exportFail(response);
		}
	}

	@Override
	public void importData(MultipartFile file) {
		// 帮派
		List<Gang> gangs = gangMapper.selectList(null);
		// 服务器
		List<Server> servers = serverMapper.selectList(null);
		// 门派
		List<School> schools = schoolMapper.selectList(null);
		// 账号
		List<Account> accounts = accountMapper.selectList(null);
		// 角色造型
		List<RoleCategory> roleCategories = roleCategoryMapper.selectList(null);

		// 查询所有角色员信息
		List<RoleAccount> list = roleAccountMapper.selectList(null);

		// 把角色信息以ID为key保存到map中
		Map<String, RoleAccount> map = transferToMap(list);

		// 读excel表

		try (InputStream inputStream = file.getInputStream()) {
			EasyExcel.read(inputStream, RoleAccountExcelData.class, new ReadListener<RoleAccountExcelData>() {
				@Override
				public void invoke(RoleAccountExcelData data, AnalysisContext context) {
					RoleAccount roleAccount = new RoleAccount();

					roleAccount.setId(data.getId());
					roleAccount.setName(data.getName());
					roleAccount.setGrade(data.getGrade());
					roleAccount.setJinbi(data.getJinbi());
					roleAccount.setScore(data.getScore());
					roleAccount.setLastUpdateTime(LocalDateTime.now());

					// 设置外键
					for (RoleCategory roleCategory : roleCategories) {
						if (roleCategory.getName().equals(data.getRole())) {
							roleAccount.setCategoryId(roleCategory.getId());

							break;
						}
					}

					// 帮派可能为空
					String dataGang = data.getGang();

					if (StringUtils.isNotEmpty(dataGang)) {
						for (Gang gang : gangs) {
							if (gang.getName().equals(dataGang)) {
								roleAccount.setGangId(gang.getId());

								break;
							}
						}
					}

					for (Server server : servers) {
						if (server.getName().equals(data.getServer())) {
							roleAccount.setServerId(server.getId());

							break;
						}
					}

					for (Account account : accounts) {
						if (account.getAccount().equals(data.getAccount())) {
							roleAccount.setAccountId(account.getId());

							break;
						}
					}

					for (School school : schools) {
						if (school.getName().equals(data.getSchool())) {
							roleAccount.setSchoolId(school.getId());

							break;
						}
					}

					// 角色存在则修改，否则插入
					if (map.containsKey(data.getId())) {
						roleAccountMapper.updateById(roleAccount);
					} else {
						roleAccountMapper.insert(roleAccount);
					}
				}

				@Override
				public void doAfterAllAnalysed(AnalysisContext context) {

				}
			}).sheet().doRead();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void insert(RoleAccountDTO roleAccountDTO) {
		boolean result = validateInsert(roleAccountDTO);

		if (result) {
			Integer grade = roleAccountDTO.getGrade(); // 角色等级
			String roleId = roleAccountDTO.getId(); // 角色ID

			// 获取当前时间生成的uuid
			String uuid = StringUtils.uuid();

			/*
			 * 1、添加角色加点和属性
			 */
			// 查询所有加点方案
			List<JiadianSchema> jiadianSchemas = jiadianSchemaMapper.selectByTypeName("角色加点");

			for (JiadianSchema schema : jiadianSchemas) {
				Integer jiadianSchema = schema.getId();

				// 1、添加角色加点
				RoleJiadian roleJiadian = new RoleJiadian();

				roleJiadian.setId(IdentifierPrefixes.PREFIX_JSJD + uuid + jiadianSchema);
				roleJiadian.setJiadianSchema(jiadianSchema);
				roleJiadian.setName(schema.getName());
				roleJiadian.setLiliang(grade);
				roleJiadian.setMinjie(grade);
				roleJiadian.setNaili(grade);
				roleJiadian.setMoli(grade);
				roleJiadian.setTizhi(grade);
				roleJiadian.setRoleId(roleId);

				// 默认开启第一套加点
				roleJiadian.setOpenStatus(jiadianSchema == 1 ? OpenStatus.OPEN.getValue()
						: OpenStatus.UN_OPEN.getValue());

				// 40级解锁第二套加点方案、90级解锁第三套加点
				roleJiadian.setUnlockStatus(jiadianSchema == 1
						|| (jiadianSchema == 2 && grade >= 40)
						|| (jiadianSchema == 3 && grade >= 90)
						? UnlockStatus.YJS.getValue() : UnlockStatus.WJS.getValue());

				roleJiadianMapper.insert(roleJiadian);

				// 2、添加的角色属性(默认值为0)
				RoleAttributeFactory roleAttributeFactory = new RoleAttributeFactory();
				RoleAttribute roleAttribute = roleAttributeFactory.produce();

				roleAttribute.setRoleId(roleId);
				roleAttribute.setJiadianId(roleJiadian.getId());
				roleAttribute.setId(IdentifierPrefixes.PREFIX_JSSX + uuid + jiadianSchema);

				roleAttributeMapper.insert(roleAttribute);
			}

			// 3、添加角色
			RoleAccount roleAccount = toEntity(roleAccountDTO);

			roleAccountMapper.insert(roleAccount);
		}
	}

	@Override
	public void deleteByIds(List<String> ids) {
		if (CollectionUtils.isNotEmpty(ids)) {
			// 1、批量删除角色属性
			roleAttributeMapper.deleteByRoleIds(ids);

			// 2、通过角色ID删除角色加点
			QueryWrapper<RoleJiadian> queryWrapper = new QueryWrapper<>();

			queryWrapper.in("role_id", ids);

			roleJiadianMapper.delete(queryWrapper);

			// 3、通过ID批量删除角色
			roleAccountMapper.deleteBatchIds(ids);

			// 4、删除角色经验、门派技能
			for (String roleId : ids) {
				Map<String, Object> columnMap = new HashMap<>();

				columnMap.put("role_id", roleId);

				roleExpMapper.deleteByMap(columnMap);
				schoolSkillMapper.deleteByMap(columnMap);
			}
		}
	}

	@Override
	public void updateById(RoleAccountDTO roleAccountDTO) {
		/*
		 * 角色账号数验证
		 */
		String roleId = roleAccountDTO.getId();
		String name = roleAccountDTO.getName();

		// 根据账号、服务器查询角色
		QueryWrapper<RoleAccount> queryWrapper = new QueryWrapper<>();

		queryWrapper.ne("id", roleId);
		queryWrapper.eq("server_id", roleAccountDTO.getServerId());
		queryWrapper.eq("account_id", roleAccountDTO.getAccountId());

		Long count = roleAccountMapper.selectCount(queryWrapper);

		// 如果角色数量达到了6，验证该账号下的角色数量
		if (count >= 6) {
			throw new GlobalException(ResponseCode.FORBIDDEN, "修改失败，该账号下已经创建了6个角色。");
		}

		// 查询同名的其他角色
		queryWrapper.clear();

		queryWrapper.ne("id", roleId);
		queryWrapper.eq("name", name);

		count = roleAccountMapper.selectCount(queryWrapper);

		// 如果查到的不是当前角色，抛出异常
		if (count > 0) {
			throw new GlobalException(ResponseCode.CONFLICT, "角色名" + name + "已存在！");
		}

		// 修改角色信息
		RoleAccount roleAccount = toEntity(roleAccountDTO);

		roleAccount.setLastUpdateTime(LocalDateTime.now());

		roleAccountMapper.updateById(roleAccount);
	}

	@Override
	public List<RoleAccount> selectAll() {
		return roleAccountRepository.selectList();
	}

	@Override
	public RoleAccount selectById(String roleId) {
		return roleAccountRepository.selectById(roleId);
	}

	@Override
	public RoleAccountVO selectRoleDetails(String roleId) {
		return roleAccountRepository.getCache(roleId);
	}

	@Override
	public Page<RoleAccount> selectByPage(Pager<RoleAccount> pager) {
		QueryWrapper<RoleAccount> queryWrapper = Pager.getQueryWrapper(pager, true);
		Page<RoleAccount> page = Pager.ofPage(pager);

		return roleAccountMapper.selectPage(page, queryWrapper);
	}

	private RoleAccount toEntity(RoleAccountDTO roleAccountDTO) {
		RoleAccount roleAccount = new RoleAccount();

		roleAccount.setId(roleAccountDTO.getId());
		roleAccount.setName(roleAccountDTO.getName());
		roleAccount.setGrade(roleAccountDTO.getGrade());
		roleAccount.setScore(roleAccountDTO.getScore());
		roleAccount.setJinbi(roleAccountDTO.getJinbi());
		roleAccount.setGangId(roleAccountDTO.getGangId());
		roleAccount.setSchoolId(roleAccountDTO.getSchoolId());
		roleAccount.setServerId(roleAccountDTO.getServerId());
		roleAccount.setBirthday(roleAccountDTO.getBirthday());
		roleAccount.setAccountId(roleAccountDTO.getAccountId());
		roleAccount.setCategoryId(roleAccountDTO.getCategoryId());

		return roleAccount;
	}

	/**
	 * 添加角色之前的验证
	 * @param roleAccountDTO RoleAccountDTO
	 * @return boolean 通过验证返回true，否则抛出异常
	 */
	private boolean validateInsert(RoleAccountDTO roleAccountDTO) {
		// 1、验证角色名是否重复
		QueryWrapper<RoleAccount> queryWrapper = new QueryWrapper<>();

		queryWrapper.eq("name", roleAccountDTO.getName());

		// 通过角色名查询角色数量
		long count = roleAccountMapper.selectCount(queryWrapper);

		// 角色名重复，抛出异常
		if (count > 0) {
			throw new GlobalException(ResponseCode.CONFLICT, "添加失败，角色名已存在。");
		}

		// 2、验证账号下同一服务器的角色数量是否达到了6
		queryWrapper.clear();

		queryWrapper.eq("account_id", roleAccountDTO.getAccountId());
		queryWrapper.eq("server_id", roleAccountDTO.getServerId());

		// 通过账号ID、服务器ID查询角色数量
		count = roleAccountMapper.selectCount(queryWrapper);

		// 角色数量不少于6个，抛出异常
		if (count >= 6) {
			throw new GlobalException(ResponseCode.CONFLICT, "添加失败，每个账号最多可以添加6个同服务器的角色~");
		}

		return true;
	}

	/**
	 * 数据库实体类转化为excel实体类
	 * @param roleAccount RoleAccount
	 * @return RoleAccountExcelData
	 */
	private RoleAccountExcelData toExcelData(RoleAccount roleAccount) {
		RoleAccountExcelData excelData = new RoleAccountExcelData();

		excelData.setId(roleAccount.getId());
		excelData.setName(roleAccount.getName());
		excelData.setGrade(roleAccount.getGrade());
		excelData.setScore(roleAccount.getScore());
		excelData.setJinbi(roleAccount.getJinbi());
		excelData.setBirthday(roleAccount.getBirthday());

		School school = schoolMapper.selectById(roleAccount.getSchoolId());
		Server server = serverMapper.selectById(roleAccount.getServerId());
		Account account = accountMapper.selectById(roleAccount.getAccountId());
		RoleCategory roleCategory = roleCategoryMapper.selectById(roleAccount.getCategoryId());

		// 设置需要转换的字段
		excelData.setSchool(school.getName());
		excelData.setServer(server.getName());
		excelData.setRole(roleCategory.getName());
		excelData.setAccount(account.getAccount());

		String gangId = roleAccount.getGangId();

		if (gangId != null) {
			Gang gang = gangMapper.selectById(gangId);

			excelData.setGang(gang.getName());
		}

		return excelData;
	}

	/**
	 * list转为map
	 * @param list List<RoleAccount>
	 * @return Map<String, RoleAccount>
	 */
	private Map<String, RoleAccount> transferToMap(List<RoleAccount> list) {
		// 把角色信息以ID为key保存到map中
		Map<String, RoleAccount> map = new HashMap<>(list.size());

		list.forEach(new Consumer<RoleAccount>() {
			@Override
			public void accept(RoleAccount roleAccount) {
				map.put(roleAccount.getId(), roleAccount);
			}
		});

		return map;
	}

}